#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <pthread.h>
#include <functional>

#include "Logger.hpp"
#include "InetAddr.hpp"
#include "Comm.hpp"
#include "ThreadPool.hpp"

static const int LisSockfdGetfault = -1;
static const int backloggefault = 8;
using task_t = std::function<void()>;


class TcpEchoServer
{
private:
    //这其实是长连接while(true)，线程和进程一般不用来执行长连接的任务，一般都是执行短连接
    void HanderIO(int sockfd, InetAddr client)
    {
        while(true)
        {
            char buffer[1024];
            buffer[0] = 0;
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);//在系统中n=0表示读到了文件结尾，在网络中表示client已经退出了
            if(n == 0)
            {
                LOG(Loglevel::INFO) << "client "
                                    << client.ToString() << " quit, me too, close fd: " << sockfd;
                break;
            }
            else if(n > 0)
            {
                buffer[n] = 0;
                std::string echo_string = "server echo# ";
                echo_string += buffer;
                LOG(Loglevel::DEBUG) << client.ToString() << "say: " << buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else
            {
                LOG(Loglevel::WARNING) << "read client "
                                       << client.ToString() << " error, sockfd : " << sockfd;
                break;
            }

        }
        close(sockfd);
    }
public:
    TcpEchoServer(uint16_t port)
        :_port(port)
        ,_listensockfd(LisSockfdGetfault)
    {}

    void Init()
    {
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensockfd < 0)
        {
            LOG(Loglevel::FATAL) << "create tcp socket error";
            exit(SOCKET_CREATE_ERR);
        }
        LOG(Loglevel::INFO) << "create tcp socket success: " << _listensockfd;

        InetAddr local(_port);
        int n = bind(_listensockfd, local.Addr(), local.Length());
        if(n != 0)
        {
            LOG(Loglevel::FATAL) << "bind socket error";
            exit(SOCKET_BIND_ERR);
        }
        LOG(Loglevel::INFO) << "bind socket success: " << _listensockfd;


        //这里的第二个参数限制的是排队等待连接的人数，但是已经连接上的客户端数量并没有作出限制
        //（也就是同时连接的客户端数量无上限，直到挤爆服务器为止）
        if(listen(_listensockfd, backloggefault) != 0)
        {
            LOG(Loglevel::FATAL) << "listen socket error";
            exit(SOCKET_LISTEN_ERR);
        }
        LOG(Loglevel::INFO) << "Listen socket success: " << _listensockfd;

    }


    class ThreadData//静态方法无法访问非静态方法和变量，所以这个类是为了给Routine传所需要的变量
    {
    public:
        ThreadData(int sockfd, const InetAddr& addr, TcpEchoServer* self)
            :_sockfd(sockfd)
            ,_clientaddr(addr)
            ,_Self(self)
        {
        }

        ~ThreadData()
        {
        }
    public:
        int _sockfd;
        InetAddr _clientaddr;
        TcpEchoServer* _Self;
    };

    static void* Routine(void* args)
    {
        ThreadData* data = static_cast<ThreadData*>(args);
        pthread_detach(pthread_self());//线程分离
        data->_Self->HanderIO(data->_sockfd,data->_clientaddr);
        delete data;
        return nullptr;
    }

    void Start()
    {
        while(true)
        {
            //多进程那里最好的方法：捕捉子进程退出时发给父进程的信号signal(SIGCHLD, SIG_IGN)

            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensockfd, (struct sockaddr*)&peer, &len);//这个套接字才是用来和对端通信的
            InetAddr clientaddr(peer);
            LOG(Loglevel::INFO) << "获取新连接成功, sockfd is : " << sockfd
                                << " client addr: " << clientaddr.ToString();
            //1、单进程,但是这时候如果有多个client，那么就只有一个退出，另一个client才能发送信息成功
            //HanderIO(sockfd, clientaddr);
            
            //2、多进程
            // pid_t fd = fork();
            // if(fd < 0)
            // {
            //     LOG(Loglevel::FATAL) << "资源不足，创建子进程失败";
            //     exit(FORK_ERR);
            // }
            // else if(fd == 0)
            // {
            //     //子进程
            //     close(_listensockfd);//关闭不需要的fd，并不会造成对应的文件关闭，因为有引用计数（子进程也有一份fd表，并且指向同一份资源）

            //     if(fork() > 0)
            //     {
            //         exit(OK);
            //     }
            //     //让子进程直接退出，将孙子进程交给操作系统管理，然后父进程等到了子进程，就直接进入下一次循环去创建下一个子进程了
            //     HanderIO(sockfd, clientaddr);
            //     exit(OK);
            // }
            // else
            // {
            //     close(sockfd);
            //     pid_t rid = waitpid(fd, nullptr, 0);
            // }


            //3、多线程
            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd, clientaddr, this);
            pthread_create(&tid, nullptr, Routine, (void*)td);

            //4、线程池（为了解决我们上面说的client连接数量没有限制，可能导致服务端爆炸的情况）
            //当然我的线程池没有设置队列插入任务上限，所以只要我插入任务速度大于线程解决问题的速度，
            //任务无限增多，任务队列无限扩容，服务端还是会爆炸
            //当然这里只是告诉大家线程池可以解决这个问题（只不过我的线程池有缺陷罢了）
            ThreadPool<task_t>::GetInstance()->Enqueue([this, sockfd, clientaddr](){
                return this->HanderIO(sockfd, clientaddr);
            });
        }
    }

    ~TcpEchoServer(){}
private:
    int _listensockfd;
    uint16_t _port;
};